package api

import (
	"fmt"
	"math/big"
	"kumachan/standalone/util"
	"kumachan/interpreter/runtime/lib/debug"
	"kumachan/interpreter/runtime/lib/container"
	"kumachan/interpreter/runtime/rt"
)


func __SeqCount(start *big.Int, n *big.Int) container.Seq {
	return container.IntervalSeq {
		Current: start,
		Bound:   big.NewInt(0).Add(start, n),
	}
}
func __SeqInclusiveRange(l *big.Int, r *big.Int) container.Seq {
	if r.Cmp(l) < 0 {
		panic("invalid sequence: lower bound bigger than upper bound")
	}
	return container.IntervalSeq {
		Current: l,
		Bound:   big.NewInt(0).Add(r, big.NewInt(1)),
	}
}

func __SeqShift(seq container.Seq) rt.EnumValue {
	var item, rest, exists = seq.Next()
	if exists {
		return rt.Some(rt.Tuple(item, rest))
	} else {
		return rt.None()
	}
}
func __SeqNil() container.Seq {
	return container.EmptySeq { ItemType: rt.ValueReflectType() }
}
func __SeqCons(head rt.Value, tail container.Seq) container.Seq {
	return container.ConsSeq {
		Head: head,
		Tail: tail,
	}
}

func __SeqToList(seq container.Seq) rt.Value {
	return container.SeqCollect(seq)
}
func __SeqWithIndex(input container.Seq) rt.Value {
	return container.MappedSeq {
		Input: container.IndexedSeq {
			Data:      input,
			NextIndex: 0,
		},
		Mapper: func(v rt.Value) rt.Value {
			var tuple = v.(rt.TupleValue)
			var index = &(tuple.Elements[1])
			*index = util.GetNumberUint((*index).(uint))
			return tuple
		},
	}
}

func __SeqMap(input container.Seq, f rt.Value, h rt.InteropContext) container.Seq {
	return container.MappedSeq {
		Input:  input,
		Mapper: func(item rt.Value) rt.Value {
			return h.Call(f, item)
		},
	}
}
func __SeqDeflateMap(input container.Seq, f rt.Value, h rt.InteropContext) container.Seq {
	return container.DeflateMappedSeq {
		Input:      input,
		MapFilter: func(item rt.Value) rt.Value {
			return h.Call(f, item)
		},
	}
}
func __SeqFilter(input container.Seq, f rt.Value, h rt.InteropContext) container.Seq {
	return container.FilteredSeq {
		Input:  input,
		Filter: func(item rt.Value) bool {
			return rt.FromBool(h.Call(f, item).(rt.EnumValue))
		},
	}
}
func __SeqFlatMap(input container.Seq, f rt.Value, h rt.InteropContext) container.Seq {
	return container.FlatMappedSeq {
		Input:   input,
		Mapper:  func(item rt.Value) rt.Value {
			return h.Call(f, item)
		},
		Current: container.EmptySeq { ItemType: input.GetItemType() },
	}
}
func __SeqChunk(input container.Seq, size *big.Int) rt.Value {
	return container.ChunkedSeq {
		ChunkSize: util.GetUintNumber(size),
		Remaining: input,
	}
}

func __SeqScan(input container.Seq, init rt.Value, f rt.Value, h rt.InteropContext) container.Seq {
	return container.ScannedSeq {
		Previous: init,
		Rest:     input,
		Reducer: func(prev rt.Value, cur rt.Value) rt.Value {
			return h.Call(f, rt.Tuple(prev, cur))
		},
	}
}
func __SeqReduce(input container.Seq, init rt.Value, f rt.Value, h rt.InteropContext) rt.Value {
	return container.SeqReduce(input, init, func(prev rt.Value, cur rt.Value) rt.Value {
		return h.Call(f, rt.Tuple(prev, cur))
	})
}
func __SeqSome(input container.Seq, f rt.Value, h rt.InteropContext) rt.EnumValue {
	return rt.ToBool(container.SeqSome(input, func(item rt.Value) bool {
		return rt.FromBool(h.Call(f, item).(rt.EnumValue))
	}))
}
func __SeqEvery(input container.Seq, f rt.Value, h rt.InteropContext) rt.EnumValue {
	return rt.ToBool(container.SeqEvery(input, func(item rt.Value) bool {
		return rt.FromBool(h.Call(f, item).(rt.EnumValue))
	}))
}

func __SeqGetFirst(input container.Seq, f rt.Value, h rt.InteropContext) rt.EnumValue {
	var value, found = container.SeqGetFirst(input, func(item rt.Value) bool {
		return rt.FromBool(h.Call(f, item).(rt.EnumValue))
	})
	if found {
		return rt.Some(value)
	} else {
		return rt.None()
	}
}
func __SeqMapFirst(input container.Seq, f rt.Value, h rt.InteropContext) rt.EnumValue {
	var value, found = container.SeqMapFirst(input, func(item rt.Value) (rt.Value, bool) {
		return rt.Unwrap(h.Call(f, item).(rt.EnumValue))
	})
	if found {
		return rt.Some(value)
	} else {
		return rt.None()
	}
}

func __SeqZip(tuple rt.TupleValue) container.Seq {
	var seq_list = make([] container.Seq, len(tuple.Elements))
	for i, v := range tuple.Elements {
		seq_list[i] = v.(container.Seq)
	}
	return container.MappedSeq {
		Input: container.ZippedSeq { SeqList: seq_list },
		Mapper: func(v rt.Value) rt.Value {
			return rt.TupleOf(v.([]rt.Value))
		},
	}
}
func __SeqZipLongest(tuple rt.TupleValue) container.Seq {
	var seq_list = make([] container.Seq, len(tuple.Elements))
	for i, v := range tuple.Elements {
		seq_list[i] = v.(container.Seq)
	}
	return container.MappedSeq {
		Input: container.ZippedLongestSeq { SeqList: seq_list },
		Mapper: func(v rt.Value) rt.Value {
			return rt.TupleOf(v.([]rt.Value))
		},
	}
}
func __SeqZipList(v rt.Value) container.Seq {
	var l = container.ListFrom(v)
	var seq_list = make([]container.Seq, l.Length())
	l.ForEach(func(i uint, v rt.Value) {
		seq_list[i] = v.(container.Seq)
	})
	return container.ZippedSeq { SeqList: seq_list }
}
func __SeqZipLongestList(v rt.Value) container.Seq {
	var l = container.ListFrom(v)
	var seq_list = make([]container.Seq, l.Length())
	l.ForEach(func(i uint, v rt.Value) {
		seq_list[i] = v.(container.Seq)
	})
	return container.ZippedLongestSeq { SeqList: seq_list }
}

func __ListToSeq(v rt.Value) container.Seq {
	return container.ListFrom(v).Iterate()
}

func __ListLength(v rt.Value) *big.Int {
	var arr = container.ListFrom(v)
	return util.GetNumberUint(arr.Length())
}

func __ListEmpty(v rt.Value) rt.EnumValue {
	var arr = container.ListFrom(v)
	return rt.ToBool(arr.Length() == 0)
}

func __ListReverse(v rt.Value) rt.Value {
	var arr = container.ListFrom(v)
	return arr.Reversed()
}
func __ListShift(v rt.Value) rt.EnumValue {
	var item, rest, ok = container.ListFrom(v).Shifted()
	if ok {
		return rt.Some(rt.Tuple(item, rest))
	} else {
		return rt.None()
	}
}
func __ListPop(v rt.Value) rt.EnumValue {
	var item, rest, ok = container.ListFrom(v).Popped()
	if ok {
		return rt.Some(rt.Tuple(item, rest))
	} else {
		return rt.None()
	}
}

func __Set(values_ rt.Value, cmp_ rt.Value, h rt.InteropContext) container.Set {
	var values = container.ListFrom(values_)
	var cmp = rt.Compare(func(a rt.Value, b rt.Value) rt.Ordering {
		var t = h.Call(cmp_, rt.Tuple(a, b))
		return rt.FromOrdering(t.(rt.EnumValue))
	})
	var s = container.NewSet(cmp)
	values.ForEach(func(i uint, item rt.Value) {
		var result, override = s.Inserted(item)
		if override {
			var item_desc = debug.InspectUntypedValue(item)
			panic(fmt.Sprintf("duplicate set item: %s", item_desc))
		}
		s = result
	})
	return s
}
// TODO: __SetItems
func __SetHas(set container.Set, v rt.Value) rt.EnumValue {
	var _, exists = set.Lookup(v)
	return rt.ToBool(exists)
}

func __Map(entries_ rt.Value, cmp_ rt.Value, h rt.InteropContext) container.Map {
	var entries = container.ListFrom(entries_)
	var cmp = rt.Compare(func(a rt.Value, b rt.Value) rt.Ordering {
		return rt.FromOrdering(h.Call(cmp_, rt.Tuple(a, b)).(rt.EnumValue))
	})
	var m = container.NewMap(cmp)
	entries.ForEach(func(_ uint, item rt.Value) {
		var key, value = rt.PairFromTuple2(item.(rt.TupleValue))
		var result, override = m.Inserted(key, value)
		if override {
			var key_desc = debug.InspectUntypedValue(key)
			panic("duplicate map key " + key_desc)
		}
		m = result
	})
	return m
}
func __MapEntries(m container.Map) ([]rt.TupleValue) {
	var entries = make([] rt.TupleValue, 0, m.Size())
	// TODO: should have an encapsulating method on container.Map
	m.AVL.Walk(func(v rt.Value) {
		var entry = v.(container.MapEntry)
		entries = append(entries, rt.Tuple(entry.Key, entry.Value))
	})
	return entries
}
func __MapGet(m container.Map, k rt.Value) rt.EnumValue {
	var v, exists = m.Lookup(k)
	if exists {
		return rt.Some(v)
	} else {
		return rt.None()
	}
}

func __MapInsert(m container.Map, entry_ rt.Value) rt.EnumValue {
	var entry = entry_.(rt.TupleValue)
	var k = entry.Elements[0]
	var v = entry.Elements[1]
	var result, override = m.Inserted(k, v)
	if !(override) {
		return rt.Some(result)
	} else {
		return rt.None()
	}
}
func __MapInsertCasual(m container.Map, entry_ rt.Value) container.Map {
	var entry = entry_.(rt.TupleValue)
	var k = entry.Elements[0]
	var v = entry.Elements[1]
	var result, _ = m.Inserted(k, v)
	return result
}

func __MapDelete(m container.Map, k rt.Value) rt.EnumValue {
	var deleted, rest, ok = m.Deleted(k)
	if ok {
		return rt.Some(rt.Tuple(deleted, rest))
	} else {
		return rt.None()
	}
}
func __MapDeleteCasual(m container.Map, k rt.Value) container.Map {
	var _, rest, _ = m.Deleted(k)
	return rest
}


